﻿using System;
using System.Linq;
using System.Security.Principal;
using System.Web.Security;
using CoProjectDF.Models.Login;
using CoProjectDF.Web.Resources;
using CoProjectModel;
using IdeaBlade.EntityModel;
using IdeaBlade.Validation;

namespace CoProjectDF
{
    /// <summary>
    /// Provides registration-related services.
    /// </summary>
    public class RegistrationServices
    {
        /// <summary>
        /// Role to which users will be added by default.  You will likely want to change this for your application.
        /// </summary>
        public const string DefaultRole = "Registered Users";

        #region VerifierEngine
        /// <summary>
        /// The DevForce VerifierEngine used for any server-side verification.
        /// </summary>
        public static VerifierEngine VerifierEngine
        {
            get
            {
                lock (_lock)
                {
                    if (_verifierEngine == null)
                    {
                        _verifierEngine = new VerifierEngine();
                        _verifierEngine.ErrorsResourceManager = ValidationErrorResources.ResourceManager;
                    }
                    return _verifierEngine;
                }
            }
            set { _verifierEngine = value; }
        }

        private static VerifierEngine _verifierEngine;
        private static object _lock = new object();
        #endregion

        #region CreateUser

        /// <summary>
        /// Entry point for ASP.NET user registration processing.  This method is called from an 
        /// InvokeServerMethodAsync call in the AuthenticationManager on the client.
        /// </summary>
        /// <param name="principal"></param>
        /// <param name="entityManager"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        [AllowRpc]
        //[DebuggerNonUserCode]
        public static CreateUserResult CreateUser(IPrincipal principal, EntityManager entityManager, params Object[] args)
        {
            if (args.Length != 2)
            {
                throw new ArgumentException("Expected arguments not provided");
            }

            RegistrationData user = args[0] as RegistrationData;
            string password = args[1] as string;

            return CreateUserCore(user, password);
        }

        /// <summary>
        /// Adds a new user.  Note that the password supplied is in clear text:  you should use SSL to help secure your Silverlight application.
        /// </summary>
        //[DebuggerNonUserCode]
        private static CreateUserResult CreateUserCore(RegistrationData user, string password)
        {
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }

            // Verify the user information.
            var results = VerifierEngine.Execute(user);
            if (!results.Ok)
            {
                //var msg = results.Errors.Select(e => e.Message).ToAggregateString(Environment.NewLine);
                return new CreateUserResult(results.Errors.ToList());
            }

            try
            {
                CoProjectEntities Manager = new CoProjectEntities(true);
                var q = Manager.Users;

                //Not needed now UserID is Indenity Field.
                //See, geekswithblogs.net/SoftwareDoneRight/archive/2011/02/15/fixing-linq-error-sequence-contains-no-elements.aspx
                //var op = q.Execute<User>().DefaultIfEmpty().Max<User>(ur => ur==null?0:ur.UserID);
                var op = q.Execute<User>().Select(ur => ur.UserID).DefaultIfEmpty().Max();

                User u = new User()
                {
                    // UserID = ++op, Identity Field
                    UserName = user.UserName,
                    Password = password,
                    FriendlyName = user.FriendlyName,
                    Email = user.Email,
                    Question = user.Question,
                    Answer = user.Answer,
                    CreatedDate = System.DateTime.UtcNow,
                    UpdatedDate = System.DateTime.UtcNow
                };

                //Server side, so is synchronous!
                Manager.AddEntity(u);
                Manager.SaveChanges();
            }
            catch (Exception ex)
            {
                var m = ex.Message;
                return new CreateUserResult(CreateUserStatus.Failure);
            }

            // Run this BEFORE creating the user to make sure roles are enabled and the default role
            // will be available.  TODO:  Be sure to use your own application-specific roles.
            //
            // If there are a problem with the role manager it is better to fail now than to have it
            // happening after the user is created
            //if (!Roles.RoleExists(DefaultRole))
            //{
            //    Roles.CreateRole(DefaultRole);
            //}

            // NOTE: ASP.NET by default uses SQL Server Express to create the user database. 
            // CreateUser will fail if you do not have SQL Server Express installed.
            //MembershipCreateStatus createStatus;
            // Membership.CreateUser(user.UserName, password, user.Email, user.Question, user.Answer, true, null, out createStatus);

            //if (createStatus != MembershipCreateStatus.Success)
            //{
            //    return new CreateUserResult(ConvertStatus(createStatus));
            // }

            // Assign it to the default role
            // This *can* fail but only if role management is disabled
            //Roles.AddUserToRole(user.UserName, DefaultRole);

            // Set its friendly name (profile setting)
            // This *can* fail but only if Web.config is configured incorrectly 
            //ProfileBase profile = ProfileBase.Create(user.UserName, true);
            //profile.SetPropertyValue("FriendlyName", user.FriendlyName);
            //profile.Save();

            return CreateUserResult.Success;
        }

        /// <summary>
        /// Convert ASP.NEt MembershipCreateStatus to our CreateUserStatus value.
        /// </summary>
        private static CreateUserStatus ConvertStatus(MembershipCreateStatus createStatus)
        {
            switch (createStatus)
            {
                case MembershipCreateStatus.Success: return CreateUserStatus.Success;
                case MembershipCreateStatus.InvalidUserName: return CreateUserStatus.InvalidUserName;
                case MembershipCreateStatus.InvalidPassword: return CreateUserStatus.InvalidPassword;
                case MembershipCreateStatus.InvalidQuestion: return CreateUserStatus.InvalidQuestion;
                case MembershipCreateStatus.InvalidAnswer: return CreateUserStatus.InvalidAnswer;
                case MembershipCreateStatus.InvalidEmail: return CreateUserStatus.InvalidEmail;
                case MembershipCreateStatus.DuplicateUserName: return CreateUserStatus.DuplicateUserName;
                case MembershipCreateStatus.DuplicateEmail: return CreateUserStatus.DuplicateEmail;
                default: return CreateUserStatus.Failure;
            }
        }
        #endregion
    }
}